﻿namespace WebPacker.Packs.Watchers
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading;
	using WebPacker.Helpers;
	using WebPacker.Logger;
	using WebPacker.Packs.Resources;

	internal sealed class ResourceChangedEventQueue : IDisposable
	{
		private readonly object syncRoot;
		private readonly IList<ResourceChangedEventArgs> events;
		private readonly Timer notifyTimer;
		private int notifyDelay = 1000;
		private bool disposed;

		public ResourceChangedEventQueue()
		{
			this.syncRoot = new object();
			this.events = new List<ResourceChangedEventArgs>();
			this.notifyTimer = new Timer(OnNotifyTimer);
		}

		public event EventHandler<ResourceChangedEventArgs> Changed;

		public int NotifyDelay
		{
			get
			{
				return notifyDelay;
			}

			set
			{
				if (value < 0)
				{
					throw new ArgumentOutOfRangeException("value");
				}

				notifyDelay = value;
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				notifyTimer.Dispose();
				disposed = true;
			}
		}

        public void AddEvent(ResourceChangedEventArgs eventArgs)
        {
            var name = eventArgs.ResourceName;

            lock (events)
            {
                if (!events.Any(x => VirtualPathHelper.Equals(x.ResourceName, name)))
                {
                    events.Add(eventArgs);
                }
            }

            if (notifyDelay == 0)
            {
                Notify();
            }
            else
            {
                notifyTimer.Change(notifyDelay, Timeout.Infinite);
            }
        }

		private void Notify()
		{
			try
			{
				if (Changed != null)
				{
					lock (events)
					{
						foreach (var eventArg in events)
						{
							Changed(this, eventArg);
						}

						events.Clear();
					}
				}
			}
			catch (Exception e)
			{
				LogFactory.GetLogger().Exception("Error at notify about changes of resources.", e);
			}
		}

		private void OnNotifyTimer(object state)
		{
			if (disposed)
			{
				return;
			}

			Notify();
		}
	}
}
